/*********************************************************************
 *  Nome : Bruno Ferrero                             N.USP 3690142
 *  Nome : Mariana Pacheco Hatamura                  N.USP 4906967 
 *  Nome : Tiago Nicolosi Bomventi                   N.USP 3690177 
 *  Curso: BCC                                 Disciplina: MAC 211
 *  Data : 5/5/2010                       Exercic. Projeto(parte 1)
 *  Compilador utilizado : gcc versao 4.1.2
 *  ******************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include "item.h"
#include "fisica.h"
#include "oceano.h"

static int tempo=0;

/* o naufrago continua na mesma direcao com 65% de chance;
  muda para as demais direcoes com 5% de chance cada uma */
int sorteioMarkov (float vx, float vy, float *newVx, float *newVy) {
    int sorteio;
    float dir, vel;
    euclidianoParaPolar (vx, vy, &dir, &vel);
    sorteio = rand() % 100;
    if (sorteio < 65) {
        limiteVelocidade (&vx, &vy);
        *newVx = vx;
        *newVy = vy;
        return sorteio;
    }
    else if (sorteio >= 65 && sorteio < 70) {
        dir = dir + (1.0*ANGtheta);
        if (dir >= 360) dir -= 360;
    }
    else if (sorteio >= 70 && sorteio < 75) {
        dir = dir + (2.0*ANGtheta);
        if (dir >= 360) dir -= 360;
    }
    else if (sorteio >= 75 && sorteio < 80) {
        dir = dir + (3.0*ANGtheta);
        if (dir >= 360) dir -= 360;
    }
    else if (sorteio >= 80 && sorteio < 85) {
        dir = dir + (4.0*ANGtheta);
        if (dir >= 360) dir -= 360;
    }
    else if (sorteio >= 85 && sorteio < 90) {
        dir = dir + (5.0*ANGtheta);
        if (dir >= 360) dir -= 360;
    }
    else if (sorteio >= 90 && sorteio < 95) {
        dir = dir + (6.0*ANGtheta);
        if (dir >= 360) dir -= 360;
    }
    else if (sorteio >= 95 && sorteio < 100) {
        dir = dir + (7.0*ANGtheta);
        if (dir >= 360) dir -= 360;
    }
    polarParaEuclidiano (dir, vel, &vx, &vy);
    /*limita a intensidade das velocidades*/
    limiteVelocidade (&vx, &vy);
    *newVx = vx;
    *newVy = vy;
    return sorteio;
}

/*atualiza a proxima posicao do naufrago utilizando para isso o seu vetor velocidade*/
void calculaProximaPosicaoNaufrago (naufrago *passageiro) {
    float vx, vy, posx, posy;
    getNaufragoPosVel (passageiro, &posx, &posy, &vx, &vy);
    setNaufragoNextPosicao (passageiro, posx + vx, posy + vy);
}

/*atualiza a proxima direcao do naufrago utilizando para isso uma cadeia de markov*/
void calculaProximaDirecaoNaufrago (naufrago *passageiro) {
    float vx, vy, posx, posy;
    float newVx, newVy;
    getNaufragoPosVel (passageiro, &posx, &posy, &vx, &vy);
    sorteioMarkov (vx, vy, &newVx, &newVy);
    setNaufragoNextVelocidade (passageiro, newVx, newVy);
}

/*calcula a proxima posicao e velocidade de todos os naufragos*/
void calculaProximoEstadoNaufragos (void) {
    naufrago *primeiro, *next;
    primeiro = nextNaufrago ();   /*atua como uma cabeca de lista circular*/
    calculaProximaPosicaoNaufrago (primeiro);
    calculaProximaDirecaoNaufrago (primeiro);
    next = nextNaufrago ();
    while (next != primeiro) {   /*enquanto nao da a volta na lista e percorre todos os naufragos*/
        calculaProximaPosicaoNaufrago (next);
        calculaProximaDirecaoNaufrago (next);
        next = nextNaufrago ();
    }
}

/*verifica se a posicao fornecida pode ser ocupada ou nao por um naufrago*/
int verificaProxPos (int posx, int posy) {
    if (posx >=0 && posx < OC_SIZE_X && posy >= 0 && posy < OC_SIZE_Y && tipoObjeto (posx, posy) == AGUA)
        return 1;
    return 0;
}

/*troca posicao e velocidade atual, por proxima posicao e proxima velocidade. eh a execucao de um passo de tempo*/
void iteracaoParaProximoPasso (void) {
    float vx, vy, posx, posy;
    float nextVx, nextVy, nextPosx, nextPosy;
    int posi, posj;
    int nextPosi, nextPosj;
    int borda1, borda2;
    naufrago *primeiro, *next;
    borda1 = borda2 = 0;
    limpaColisoes ();             /*desmarca as colisoes anteriores da matriz */
laco:
    primeiro = nextNaufrago ();   /*atua como uma cabeca de lista circular*/
    next = primeiro;
    do {
        if (next == NULL) break;  /*nao ha nenhum naufrago na lista*/
        getNaufragoPosVel (next, &posx, &posy, &vx, &vy);
        posi = NEAREST(posx);
        posj = NEAREST(posy);
        if (tipoObjeto (posi, posj) == COLISAO) {/*objeto ja sofreu colisao*/
            next = nextNaufrago ();
            continue;
        }
        atualizaOceano (posi, posj, AGUA);
        getNaufragoNextPosVel (next, &nextPosx, &nextPosy, &nextVx, &nextVy);  /*pega proxima posicao e velocidade*/
        nextPosi = NEAREST (nextPosx);
        nextPosj = NEAREST (nextPosy);
        /*verifica se pode andar*/
        if (verificaProxPos (nextPosi, nextPosj)) {
            /*atualiza posicao e velocidade*/
            setNaufragoPosicao (next, nextPosx, nextPosy);
            setNaufragoVelocidade (next, nextVx, nextVy);
            atualizaOceano (nextPosi, nextPosj, NAUFRAGO);
        }
        else {
            if (nextPosi <0 || nextPosi >= OC_SIZE_X || nextPosj < 0 || nextPosj >= OC_SIZE_Y ) {
                /*saiu pela borda, gera um novo*/
                /*printf ("saiu pela borda (%d, %d) -----> ", nextPosi, nextPosj);*/
                sorteiaBordas (&borda1, &borda2);
                while (tipoObjeto (borda1, borda2) != AGUA)
                    sorteiaBordas (&borda1, &borda2);
                /*printf ("entrou pela borda (%d, %d)\n", borda1, borda2);*/
                setNaufragoPosicao (next,(float) borda1,(float) borda2); /*gerando um novo naufrago*/
                sorteiaVelocidade (&vx, &vy);
                setNaufragoVelocidade (next, vx, vy);
                atualizaOceano (borda1, borda2, NAUFRAGO);
            }
            else if (tipoObjeto (nextPosi, nextPosj) == CORAL) {
                /*naufrago morre*/
                /*printf ("morreu naufrago em (%d, %d)\n", nextPosi, nextPosj);*/
                removeNaufrago (next);
                if (next == primeiro)
                    goto laco;
            }
            else if (tipoObjeto (nextPosi, nextPosj) == NAUFRAGO || tipoObjeto (nextPosi, nextPosj) == COLISAO) {
                /*COLISAO ELASTICA*/
                /*printf ("colidiu naufrago em (%d, %d)\n", nextPosi, nextPosj);*/
                atualizaOceano (nextPosi, nextPosj, COLISAO); 
                transfereMomento (getNaufrago(nextPosi, nextPosj), next);
                /*marca na matriz onde estava antes de colidir*/
                atualizaOceano (posi, posj, COLISAO);
            }
        }
        next = nextNaufrago ();
    } while (next != primeiro && numNaufragosNoVetor() > 0);   /*enquanto nao da a volta na lista e percorre todos os naufragos*/
}

/*transferencia de momento assumindo colisao elastica e que cada naufrago tem o mesmo peso*/
void transfereMomento (naufrago * passivo, naufrago * ativo) {
    /*formula: v1f = 2m2v2i/(m1 + m2) + v1i(m1 - m2)/(m1 + m2)*/
    /*         v2f = 2m1v1i/(m1 + m2) + v2i(m2 - m1)/(m2 + m1)*/
    float pposx, pposy, pvx, pvy;
    float aposx, aposy, avx, avy;
    getNaufragoPosVel (ativo, &aposx, &aposy, &avx, &avy);
    getNaufragoPosVel (passivo, &pposx, &pposy, &pvx, &pvy);
    setNaufragoVelocidade (ativo, pvx, pvy);   /*troca as velocidades*/
    setNaufragoVelocidade (passivo, avx, avy);
}

/*limita as velocidades*/
void limiteVelocidade (float *vx, float *vy) {
    if (*vx > VXMAX) *vx = VXMAX;
    else if (*vx < -VXMAX) *vx = -VXMAX;
    if (*vy > VYMAX) *vy = VYMAX;
    else if (*vy < -VYMAX) *vy = -VYMAX;
}

/*transforma de coordenada polar para coordenada euclidiana*/
void polarParaEuclidiano (float dir, float vel, float *vx, float *vy) {
    *vx = vel * cos (GRAUS2RAD(dir));
    *vy = vel * sin (GRAUS2RAD(dir));
}

/*transforma de coordenada euclidiana para coordenada polar*/
void euclidianoParaPolar (float vx, float vy, float *dir, float *vel) {
    *vel = sqrt(vx*vx + vy*vy);
    if (*vel >= EPS) {
        /*decompoe a direcao*/
        *dir = RAD2GRAUS (atan2(vy, vx));
        if (*dir < 0) *dir += 360.0;
    }
}

/*incrementa o tempo do jogo*/
void incrementaTempo (int incremento) {
	tempo+=incremento;
}

/*retorna o tempo*/
int getTempo (void) {
	return tempo;
}

